import { error } from '../../../output.js'
import { getVariable } from '../../../tools/variable.js'
import { toToken } from '../../stream/token.js'
import parser from '../../parser/index.js'
import interpreter, { init as runtimeInit } from '../../interpreter/index.js'
import call_run from './call.js'
import Runtime from '../../../runtime.js'

const run = async (runtime, ast, node) => {
  if (node.type !== 'ForStatement') return

  const { init, test, update, consequent } = node

  const newRuntime = new Runtime()
  newRuntime.parent = runtime
  newRuntime.config = runtime.config

  runtimeInit(newRuntime, {
    filepath: ast.filepath,
    type: 'function'
  })

  const _init = async () => {
    const { type, body } = init
    if (type === 'BlockStatement') {
      const _tokens = toToken(body)
      const _ast = parser(_tokens, ast.filepath, 'function')
      return await interpreter(newRuntime, _ast)
    } else {
      error(`Unsupported type: ${ type }`)
    }
  }

  const _run = async () => {
    const { type, body } = consequent
    if (type === 'BlockStatement') {
      const _tokens = toToken(body)
      const _ast = parser(_tokens, ast.filepath, 'function')
      const _runtime = new Runtime()

      runtimeInit(_runtime, _ast)

      _runtime.parent = newRuntime
      _runtime.config = newRuntime.config

      return await interpreter(_runtime, _ast)
    } else if (type === 'ExpressionStatement') {
      await call_run(newRuntime, ast, body)
    } else {
      error(`Unsupported type: ${ type }`)
    }
  }

  const _update = async () => {
    const { type, body } = update
    if (type === 'BlockStatement') {
      const _tokens = toToken(body)
      const _ast = parser(_tokens, ast.filepath, 'function')
      return await interpreter(newRuntime, _ast)
    } else {
      error(`Unsupported type: ${ type }`)
    }
  }

  const _test = async () => {
    const { left, right, operator, type } = test

    if (type === 'BinaryExpression') {
      let leftValue = left?.value
      let rightValue = right?.value

      if (left.type === 'Identifier') {
        leftValue = getVariable(newRuntime, left.name, ast.filepath)
        if (leftValue === null) {
          error(`Variable ${ left.name } is not defined`)
        }
      }

      if (right.type === 'Identifier') {
        rightValue = getVariable(newRuntime, right.name, ast.filepath)
        if (rightValue === null) {
          error(`Variable ${ right.name } is not defined`)
        }
      }

      if (typeof leftValue !== 'number' && typeof leftValue !== 'string') {
        error(`Unsupported left value: ${ left.type === 'Identifier' ? left.name : leftValue }`)
      }

      if (typeof rightValue !== 'number' && typeof rightValue !== 'string') {
        error(`Unsupported right value: ${ right.type === 'Identifier' ? right.name : rightValue }`)
      }

      if (typeof leftValue === 'number' && typeof rightValue === 'string') {
        error(`Unsupported expression: ${ left.type === 'Identifier' ? left.name : leftValue } ${ operator } ${ right.type === 'Identifier' ? right.name : `"${ rightValue }"` }`)
      }

      if (typeof leftValue === 'string' && typeof rightValue === 'number') {
        error(`Unsupported expression: ${ left.type === 'Identifier' ? left.name : `"${ leftValue }"` } ${ operator } ${ right.type === 'Identifier' ? right.name : rightValue }`)
      }

      if (typeof leftValue === 'string' && typeof rightValue === 'string') {
        if (operator === '==') {
          if (leftValue === rightValue) {
            await _run()
            return true
          }
        } else if (operator === '!=') {
          if (leftValue !== rightValue) {
            await _run()
            return true
          }
        } else {
          error(`Unsupported expression: "${ leftValue }" ${ operator } "${ rightValue }"`)
        }
      }

      if (typeof leftValue === 'number' && typeof rightValue === 'number') {
        if (operator === '>') {
          if (leftValue > rightValue) {
            await _run()
            return true
          }
        } else if (operator === '<') {
          if (leftValue < rightValue) {
            await _run()
            return true
          }
        } else if (operator === '>=') {
          if (leftValue >= rightValue) {
            await _run()
            return true
          }
        } else if (operator === '<=') {
          if (leftValue <= rightValue) {
            await _run()
            return true
          }
        } else if (operator === '==') {
          if (leftValue === rightValue) {
            await _run()
            return true
          }
        } else if (operator === '!=') {
          if (leftValue !== rightValue) {
            await _run()
            return true
          }
        } else {
          error(`Unsupported expression: ${ leftValue } ${ operator } ${ rightValue }`)
        }
      }
    } else {
      error(`Unsupported expression: ${ type }`)
    }
  }

  await _init()

  while (true) {
    if (!(await _test())) break
    await _update()
  }
}

export default run